package dvb.reddwarf.profiler;

import java.beans.PropertyChangeEvent;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

import com.sun.sgs.auth.Identity;
import com.sun.sgs.impl.sharedutil.PropertiesWrapper;
import com.sun.sgs.kernel.ComponentRegistry;
import com.sun.sgs.profile.ProfileListener;
import com.sun.sgs.profile.ProfileReport;

/**
 * Profiler collects data about running tasks
 * and periodically flushes summary to a file
 * @author dvb
 */
public class PeriodicProfilerListener implements ProfileListener {

    private static final String PROPERTY_NAME = "dvb.reddwarf.profiler.PeriodicProfilerListener";

    public static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("00.00");

    private final int topListSize;

    private final String tasksTopFilename;

    private final int period;

    volatile ConcurrentHashMap<String, TaskRecord> tasks;

    private boolean stop = false;

    public PeriodicProfilerListener(Properties properties, Identity identity, ComponentRegistry registry) {

        PropertiesWrapper pw = new PropertiesWrapper(properties);

        tasksTopFilename = pw.getProperty(PROPERTY_NAME +".tasksTopFilename", "top.log");

        period = pw.getIntProperty(PROPERTY_NAME +".period", 60*1000);

        topListSize = pw.getIntProperty(PROPERTY_NAME +".topListSize", 20);

        tasks = new ConcurrentHashMap<String, TaskRecord>();

        new Thread(new PeriodicProfilerRunnable(), "reddwarf periodic profiler thread").start();
    }


    /**
     * Writes most frequent task types info to file
     * @throws IOException
     */
    private void writeTop() throws IOException {
        BufferedWriter tasksLog = new BufferedWriter(new FileWriter(new File(tasksTopFilename), true));
        ConcurrentHashMap<String, TaskRecord> oldTasks = tasks;
        tasks = new ConcurrentHashMap<String, TaskRecord>();
        ArrayList<TaskRecord> list = new ArrayList<TaskRecord>(oldTasks.values());
        Collections.sort(list, new Comparator<TaskRecord>() {
            @Override
            public int compare(TaskRecord r1, TaskRecord r2) {
                return - (r1.totalTime.intValue()>r2.totalTime.intValue()?1:(r1.totalTime.intValue()<r2.totalTime.intValue()?-1:0));
                //return - (r1.getFailedPercent()>r2.getFailedPercent()?1:(r1.getFailedPercent()<r2.getFailedPercent()?-1:0));
            }
        });
        tasksLog.write("*** "+new Date());
        tasksLog.newLine();
        for (int i = 0; i < list.size(); i++) {
            TaskRecord task = list.get(i);
            if (i < topListSize || task.getFailedPercent() > 0.009) {
                tasksLog.write(task.toString());
                tasksLog.newLine();
            }
        }
        tasksLog.flush();
    }

    public void propertyChange(PropertyChangeEvent event) {
    }

    public void report(ProfileReport report) {
        String type = report.getTask().getBaseTaskType() + (report.wasTaskTransactional()?" (T)":" (N)");
        if (!tasks.containsKey(type)) {
            tasks.putIfAbsent(type, new TaskRecord(type));
        }
        TaskRecord taskRecord = tasks.get(type);
        taskRecord.report(report.getRunningTime(), report.wasTaskSuccessful());
    }

    public void shutdown() {
        stop = true;
    }

    private class PeriodicProfilerRunnable implements Runnable {
        @Override
        public void run() {
            while (!stop) {
                try {
                    writeTop();
                } catch (IOException e) {
                    e.printStackTrace();
                }

                try {
                    Thread.sleep(period);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
